from typing import Optional
from fastapi import Depends, HTTPException, status, Request
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from jose import JWTError, jwt
import re

from server.db_manager import db_manager
from server.models.user_model import User
from server.utils.auth_utils import AuthUtils
from server.utils.api_key_utils import (
    validate_api_key, get_client_info, api_key_tracker,
    APIKeyConfig, APIKeyUsageTracker
)

# 定义OAuth2密码承载器，指定token URL
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=False)

# 公开路径列表，无需登录即可访问
PUBLIC_PATHS = [
    r"^/api/auth/token$",            # 登录
    r"^/api/auth/check-first-run$",  # 检查是否首次运行
    r"^/api/auth/initialize$",       # 初始化系统
    r"^/api$",                      # Health Check
    r"^/api/system/health$",        # Health Check
    r"^/api/system/info$",          # 获取系统信息配置
]

# 获取数据库会话
def get_db():
    db = db_manager.get_session()
    try:
        yield db
    finally:
        db.close()

# 获取当前用户
async def get_current_user(token: str | None = Depends(oauth2_scheme), db: Session = Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="无效的凭证",
        headers={"WWW-Authenticate": "Bearer"},
    )

    # 允许无token访问公开路径
    if token is None:
        return None

    try:
        # 验证token
        payload = AuthUtils.verify_access_token(token)
        user_id = payload.get("sub")
        if user_id is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    except ValueError as e:
        # 捕获AuthUtils.verify_access_token可能抛出的ValueError
        # 例如令牌过期或无效
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=str(e),  # 将错误信息直接传递给客户端
            headers={"WWW-Authenticate": "Bearer"},
        )

    # 查找用户
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise credentials_exception

    return user

# 获取已登录用户（抛出401如果未登录）
async def get_required_user(user: User | None = Depends(get_current_user), request: Request = None):
    """
    获取已登录用户，支持API Key用户和传统用户认证

    Args:
        user: 从get_current_user获取的用户
        request: FastAPI请求对象

    Returns:
        用户对象
    """
    # 首先检查是否已经有API Key用户通过中间件认证
    if hasattr(request.state, 'user') and hasattr(request.state, 'is_api_key_user'):
        if request.state.is_api_key_user:
            return request.state.user

    # 检查传统用户认证
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="请登录后再访问",
            headers={"WWW-Authenticate": "Bearer"},
        )
    return user

# 获取管理员用户
async def get_admin_user(current_user: User = Depends(get_required_user), request: Request = None):
    """获取管理员用户，不支持API Key用户访问管理员接口"""
    # 检查是否为API Key用户
    if hasattr(request.state, 'is_api_key_user') and request.state.is_api_key_user:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="API Key用户无权访问管理员接口",
        )

    if current_user.role not in ["admin", "superadmin"]:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="需要管理员权限",
        )
    return current_user

# 获取超级管理员用户
async def get_superadmin_user(current_user: User = Depends(get_required_user), request: Request = None):
    """获取超级管理员用户，不支持API Key用户访问超级管理员接口"""
    # 检查是否为API Key用户
    if hasattr(request.state, 'is_api_key_user') and request.state.is_api_key_user:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="API Key用户无权访问超级管理员接口",
        )

    if current_user.role != "superadmin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="需要超级管理员权限",
        )
    return current_user

# 检查路径是否为公开路径
def is_public_path(path: str) -> bool:
    path = path.rstrip('/')  # 去除尾部斜杠以便于匹配
    for pattern in PUBLIC_PATHS:
        if re.match(pattern, path):
            return True
    return False


# 检查路径是否允许API Key访问（包括公开路径）
def is_api_key_allowed_path(path: str) -> bool:
    """检查路径是否允许API Key访问"""
    # 公开路径默认允许API Key访问
    if is_public_path(path):
        return True

    # 检查是否在API Key专用路径列表中
    return APIKeyConfig.is_path_allowed_for_api_key(path)


async def get_api_key_user(request: Request) -> Optional[User]:
    """
    通过API Key获取用户（模拟用户）

    Returns:
        如果API Key验证通过，返回一个模拟的用户对象
        如果没有提供API Key，返回None

    Raises:
        HTTPException: 如果提供了API Key但验证失败
    """
    if not APIKeyConfig.API_KEY_ENABLED:
        return None

    # 首先检查是否真的提供了API Key
    provided_api_key = request.headers.get("X-API-Key") or request.query_params.get("api_key")
    if not provided_api_key:
        # 没有提供API Key，返回None让其他鉴权逻辑处理
        return None

    # 提供了API Key，现在进行验证
    try:
        api_key = validate_api_key(request)  # 这会抛出HTTPException如果无效
        if api_key:
            # API Key验证通过，创建一个模拟的用户对象
            # 这个用户具有有限的权限，仅用于标识第三方调用
            from server.models.user_model import User

            # 创建一个具有基本权限的虚拟用户
            api_user = User(
                id=0,  # 特殊ID，标识为API Key用户
                username="third_party_api",
                password_hash="api_key_authenticated",
                role="api_user"  # 特殊角色
            )
            return api_user
    except HTTPException:
        # 提供了API Key但验证失败，重新抛出异常
        # 不再"吞掉"异常，而是让中间件处理
        raise

    return None
